home *** CD-ROM | disk | FTP | other *** search
/ Java Programmer's Toolkit / Java Programmer's Toolkit.iso / applets / missile / missilec.jav < prev    next >
Encoding:
Text File  |  1995-12-15  |  21.8 KB  |  1,110 lines

  1. /* MissileCommando.java - based on the arcade game Missile Command. */
  2.  
  3. /* 
  4.  * Copyright (C) 1995 Mark Boyns <boyns@sdsu.edu>
  5.  *
  6.  * MissileCommando
  7.  * <URL:http://www.sdsu.edu/~boyns/java/mc/>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  */
  23.  
  24. import java.applet.*;
  25. import java.awt.*;
  26. import java.util.Enumeration;
  27. import java.util.Vector;
  28.  
  29.  
  30. /* Abstract class used for moving objects, etc. */
  31. abstract class Thing extends Thread
  32. {
  33.     MissileCommando parent;
  34.     public int X;
  35.     public int Y;
  36.     Color color;
  37.     
  38.     abstract void paint (Graphics g); /* paint the object */
  39.     abstract void erase (Graphics g); /* erase the object */
  40.     abstract boolean hit (Missile m); /* did this missile hit the object? */
  41.     abstract void explode (); /* explode the object */
  42. }
  43.  
  44.  
  45. /* A simple rectangular base.  This object just sits around waiting to
  46.    explode. */
  47. class Base extends Thing
  48. {
  49.     private int W = 0;
  50.     private int H = 0;
  51.     
  52.     Base (MissileCommando parent, Color color, int x, int y, int w, int h)
  53.     {
  54.     this.parent = parent;
  55.     X = x;
  56.     Y = y;
  57.     this.color = color;
  58.     
  59.     W = w;
  60.     H = h;
  61.  
  62.     start ();
  63.     }
  64.  
  65.     public void run ()
  66.     {
  67.     Thread.currentThread().setPriority (Thread.MIN_PRIORITY);
  68.     waitToDie ();
  69.     }
  70.  
  71.     public synchronized void waitToDie ()
  72.     {
  73.     try 
  74.     {
  75.         wait ();
  76.     }
  77.     catch (InterruptedException e)
  78.     {
  79.     }
  80.     }
  81.  
  82.     public synchronized void timeToDie ()
  83.     {
  84.     notify ();
  85.     }
  86.     
  87.     public boolean hit (Missile m)
  88.     {
  89.     return (m.X >= X && m.X <= (X + W)
  90.         && m.Y >= Y && m.Y <= (Y + H));
  91.     }
  92.  
  93.     public void explode ()
  94.     {
  95.     timeToDie ();
  96.     stop ();
  97.     }
  98.     
  99.     public void paint (Graphics g)
  100.     {
  101.     g.setColor (color);
  102.     g.fillRect (X, Y, W, H);
  103.     }
  104.  
  105.     public void erase (Graphics g)
  106.     {
  107.     g.setColor (Color.lightGray);
  108.     g.fillRect (X, Y, W, H);
  109.     }
  110. }
  111.  
  112.  
  113. /* A missile which moves from X1,Y1 to X2,Y2 with velocity V.  A
  114.    missile can randomly replicate itself into 3 missiles. */
  115. class Missile extends Thing
  116. {
  117.     private boolean paintme = false;
  118.     private int X1 = 0;
  119.     private int Y1 = 0;
  120.     private int X2 = 0;
  121.     private int Y2 = 0;
  122.     private int V = 0;
  123.     private float m = 0;
  124.     private float b = 0;
  125.     private float x = 0;
  126.     private float y = 0;
  127.     private boolean replicate = false;
  128.     
  129.     Missile (MissileCommando parent, Color color, int x1, int y1, int x2, int y2, int v)
  130.     {
  131.     this.parent = parent;
  132.     X = X1;
  133.     Y = Y1;
  134.     this.color = color;
  135.     
  136.     X1 = x1;
  137.     Y1 = y1;
  138.     X2 = x2;
  139.     Y2 = y2;
  140.     V = v;
  141.     start ();
  142.     }
  143.  
  144.     public void run ()
  145.     {
  146.     Thread.currentThread().setPriority (Thread.MIN_PRIORITY);
  147.     
  148.     m = (float) (Y2 - Y1) / (X2 - X1);
  149.     b = Y1 - (m*X1);
  150.  
  151.     x = X1;
  152.     y = Y1;
  153.  
  154.     /* 10% chance this missile will replicate. */
  155.     replicate = Math.random () > 0.90;
  156.     if (replicate)
  157.     {
  158.         color = Color.magenta;
  159.     }
  160.     
  161.     while (y <= Y2)
  162.     {
  163.         /* 25% chance this missile will replicate now. */
  164.         if (replicate && y > Y1+5*V && Math.random () > 0.75)
  165.         {
  166.         for (int i = 0; i < 3; i++)
  167.         {
  168.             parent.createMissile (X, Y, V);
  169.         }
  170.         /* stop the current missile */
  171.         break;
  172.         }
  173.         
  174.         paintme = true;
  175.         parent.repaint ();
  176.         
  177.         try 
  178.         {
  179.         Thread.sleep (100);
  180.         }
  181.         catch (InterruptedException e)
  182.         {
  183.         }
  184.     }
  185.  
  186.     paintme = true;
  187.     parent.repaint ();
  188.     }
  189.  
  190.     public boolean hit (Missile m)
  191.     {
  192.     return m.X == X && m.Y == Y;
  193.     }
  194.     
  195.     public void explode ()
  196.     {
  197.     stop ();
  198.     }
  199.     
  200.     public void paint (Graphics g)
  201.     {
  202.     if (paintme)
  203.     {
  204.         g.setColor (Color.lightGray);
  205.         g.drawLine (X1, Y1, X, Y);
  206.  
  207.         y += V;
  208.         x = (y - b) / m;
  209.  
  210.         X = (int)x;
  211.         Y = (int)y;
  212.         
  213.         g.setColor (color);
  214.         g.drawLine (X1, Y1, X, Y);
  215.         
  216.         paintme = false;
  217.     }
  218.     }
  219.  
  220.     public void erase (Graphics g)
  221.     {
  222.     g.setColor (Color.lightGray);
  223.     g.drawLine (X1, Y1, X, Y);
  224.     }
  225. }
  226.  
  227.  
  228. /* Generic explosion which draws a circle that grows and shrinks.  The
  229.    explosion is draw at X,Y and has a maximum size of S. */
  230. class Explosion extends Thing
  231. {
  232.     private boolean paintme = false;
  233.     private int S = 0;
  234.     public int size = 0;
  235.     private int scale = 0;
  236.  
  237.     Explosion (MissileCommando parent, Color color, int x, int y, int s)
  238.     {
  239.     this.parent = parent;
  240.     X = x;
  241.     Y = y;
  242.     this.color = color;
  243.     
  244.     S = s;
  245.     start ();
  246.     }
  247.  
  248.     public void run ()
  249.     {
  250.     Thread.currentThread().setPriority (Thread.MIN_PRIORITY);
  251.  
  252.     /* grow */
  253.     scale = 10;
  254.     size = 1;
  255.     do
  256.     {
  257.         paintme = true;
  258.         parent.repaint ();
  259.         
  260.         try 
  261.         {
  262.         Thread.sleep (50);
  263.         }
  264.         catch (InterruptedException e)
  265.             {
  266.             }
  267.     }
  268.     while (size <= S);
  269.  
  270.     /* shrink */
  271.     scale = -10;
  272.     do
  273.     {
  274.         paintme = true;
  275.         parent.repaint ();
  276.         try
  277.         {
  278.         Thread.sleep (100);
  279.         }
  280.         catch (InterruptedException e)
  281.             {
  282.             }
  283.     }
  284.     while (size >= 0);
  285.  
  286.     paintme = true;
  287.     parent.repaint ();
  288.     }
  289.  
  290.     public boolean hit (Missile m)
  291.     {
  292.     return (m.X >= (X - size/2) && m.X <= (X + size/2)
  293.         && m.Y >= (Y - size/2) && m.Y <= (Y + size/2));
  294.     }
  295.     
  296.     public void explode ()
  297.     {
  298.     }
  299.     
  300.     public void paint (Graphics g)
  301.     {
  302.     if (paintme)
  303.     {
  304.         if (scale < 0)
  305.         {
  306.         g.setColor (Color.lightGray);
  307.         g.fillOval (X - size/2, Y - size/2, size, size);
  308.         }
  309.         
  310.         size += scale;
  311.         
  312.         g.setColor (color);
  313.         g.fillOval (X - size/2, Y - size/2, size, size);
  314.         paintme = false;
  315.     }
  316.     }
  317.  
  318.     public void erase (Graphics g)
  319.     {
  320.     }
  321. }
  322.  
  323.  
  324. /* A shot explosion base on Explosion. */
  325. class ShotExplosion extends Explosion
  326. {
  327.     ShotExplosion (MissileCommando parent, int x, int y)
  328.     {
  329.     super (parent, Color.black, x, y, 60);
  330.     }
  331. }
  332.  
  333.  
  334. /* A base explosion base on Explosion. */
  335. class BaseExplosion extends Explosion
  336. {
  337.     BaseExplosion (MissileCommando parent, int x, int y)
  338.     {
  339.     super (parent, Color.red, x, y, 100);
  340.     }
  341. }
  342.  
  343.  
  344. /* Draw a blinking message. */
  345. class Message extends Thing
  346. {
  347.     private String message;
  348.     private int blinks;
  349.     private int delay;
  350.     private int blinkCount;
  351.     
  352.     Message (MissileCommando parent, String message, int x, int y, int blinks, int delay)
  353.     {
  354.     this.parent = parent;
  355.     this.message = message;
  356.     X = x;
  357.     Y = y;
  358.     this.blinks = blinks;
  359.     this.delay = delay;
  360.     color = Color.black;
  361.  
  362.     start ();
  363.     }
  364.  
  365.     Message (MissileCommando parent, String message, int x, int y, int blinks)
  366.     {
  367.     this (parent, message, x, y, blinks, 500);
  368.     }
  369.     
  370.     Message (MissileCommando parent, String message, int x, int y)
  371.     {
  372.     this (parent, message, x, y, 3, 500);
  373.     }
  374.  
  375.     public void run ()
  376.     {
  377.     Thread.currentThread().setPriority (Thread.MIN_PRIORITY);
  378.     
  379.     for (blinkCount = 0; blinkCount < 2*blinks; blinkCount++)
  380.     {
  381.         parent.repaint ();
  382.         try 
  383.         {
  384.         Thread.sleep (delay);
  385.         }
  386.         catch (InterruptedException e)
  387.             {
  388.             }
  389.     }
  390.  
  391.     parent.repaint ();
  392.     }
  393.  
  394.     public void paint (Graphics g)
  395.     {
  396.     if ((blinkCount % 2) == 0)
  397.     {
  398.         g.setColor (color);
  399.     }
  400.     else
  401.     {
  402.         g.setColor (Color.lightGray);
  403.     }
  404.     g.drawString (message, X, Y);
  405.     }
  406.  
  407.     public void erase (Graphics g)
  408.     {
  409.     g.setColor (Color.lightGray);
  410.     g.drawString (message, X, Y);
  411.     }
  412.  
  413.     public boolean hit (Missile m)
  414.     {
  415.     return false;
  416.     }
  417.  
  418.     public void explode ()
  419.     {
  420.     }
  421. }
  422.  
  423.  
  424. /* Semaphore class used to synchronize threads. */
  425. class Semaphore
  426. {
  427.     private boolean taken;
  428.  
  429.     Semaphore ()
  430.     {
  431.     taken = false;
  432.     }
  433.  
  434.     Semaphore (boolean taken)
  435.     {
  436.     this.taken = taken;
  437.     }
  438.     
  439.     public synchronized void take ()
  440.     {
  441.     while (taken)
  442.     {
  443.         try
  444.         {
  445.         wait ();
  446.         }
  447.         catch (Exception e)
  448.         {
  449.         }
  450.     }
  451.     taken = true;
  452.     }
  453.  
  454.     public synchronized boolean peek ()
  455.     {
  456.     return taken;
  457.     }
  458.     
  459.     public synchronized void give ()
  460.     {
  461.     taken = false;
  462.     notify ();
  463.     }
  464. }
  465.  
  466.  
  467. /* The main applet. */
  468. public class MissileCommando extends java.applet.Applet implements Runnable
  469. {
  470.     /* Screen sizes. */
  471.     private final int SCORE_WIDTH = 70;
  472.     private final int WORLD_WIDTH = 430;
  473.     private final int WORLD_HEIGHT = 300;
  474.  
  475.     /* Base parameters. */
  476.     private final int BASES = 5;
  477.     private final int BASE_SPACING = 30;
  478.     private final int BASE_WIDTH = 50;
  479.     private final int BASE_HEIGHT = 40;
  480.  
  481.     /* Points */
  482.     private final int POINTS_MISSILE = 100;
  483.     private final int POINTS_EXTRA_SHOTS = 50;
  484.     private final int POINTS_BASE = 100;
  485.     private final int POINTS_NEW_BASE = 5000;
  486.  
  487.     /* Limit the number of concurrent shots. */
  488.     private final int MAX_SHOTS = 10;
  489.     private int shots = 0;
  490.  
  491.     /* The sounds. */
  492.     private AudioClip startSound = null;
  493.     private AudioClip applauseSound = null;
  494.     private AudioClip missileSound = null;
  495.     private AudioClip shotExplosionSound = null;
  496.     private AudioClip missileExplosionSound = null;
  497.     private AudioClip baseExplosionSound = null;
  498.     private AudioClip music = null;
  499.  
  500.     /* State variables. */
  501.     private boolean playing = false;
  502.     private boolean clearScreen = false;
  503.     private boolean loadingSounds = false;
  504.  
  505.     /* Current game parameters. */
  506.     private int score = 0;
  507.     private int level = 0;
  508.     private int speed = 0;
  509.     private int delay = 0;
  510.     private int missileCount = 0;
  511.     private int shotCount = 0;
  512.  
  513.     /* Synchronization semaphores. */
  514.     private Semaphore missileSemaphore;
  515.     private Semaphore messageSemaphore;
  516.  
  517.     /* Fonts */
  518.     private Font font;
  519.     private FontMetrics fontMetrics;
  520.  
  521.     /* Strings */
  522.     private String scoreString = "Score";
  523.     private String levelString = "Level";
  524.     private String shotString = "Shots";
  525.     private String welcomeString = "Click to start";
  526.     private String loadingString = "Loading sounds...";
  527.  
  528.     /* Vector used to keep track of all moving objects (Things). */
  529.     private Vector things;
  530.  
  531.     /* The main applet's thread. */
  532.     private Thread thread = null;
  533.  
  534.  
  535.     /* Initialize the applet. */
  536.     public void init ()
  537.     {
  538.     font = new Font ("TimesRoman", Font.BOLD, 24);
  539.     fontMetrics = getFontMetrics (font);
  540.     setFont (font);
  541.     resize (WORLD_WIDTH + SCORE_WIDTH, WORLD_HEIGHT);
  542.     }
  543.  
  544.  
  545.     /* Start a new game. */
  546.     public synchronized void newGame ()
  547.     {
  548.     playing = true;
  549.     clearScreen = true;
  550.  
  551.     stopThreads ();
  552.     
  553.     thread = new Thread (this);
  554.     thread.start ();
  555.     }
  556.  
  557.  
  558.     /* The game engine. */
  559.     public void run ()
  560.     {
  561.     int n;
  562.     int newBases = 0;
  563.  
  564.     Thread.currentThread().setPriority (Thread.MIN_PRIORITY);
  565.     
  566.     score = 0;
  567.     shotCount = 0;
  568.     shots = 0;
  569.  
  570.     things = new Vector (32);
  571.     missileSemaphore = new Semaphore ();
  572.     messageSemaphore = new Semaphore ();
  573.  
  574.     getSounds ();
  575.     createBases ();
  576.  
  577.     if (music != null)
  578.     {
  579.         music.loop ();
  580.     }
  581.     
  582.     for (level = 1; ; level++)
  583.     {
  584.         if (level == 1)
  585.         {
  586.         if (startSound != null)
  587.         {
  588.             startSound.play ();
  589.         }
  590.         }
  591.         else
  592.         {
  593.         if (applauseSound != null)
  594.         {
  595.             applauseSound.play ();
  596.         }
  597.         }
  598.         messageSemaphore.take ();
  599.         createMessage ("Level " + level);
  600.         messageSemaphore.take ();
  601.         messageSemaphore.give ();
  602.  
  603.         /* Missile speed. */
  604.         speed = 5 + (level - 1);
  605.         if (speed > (WORLD_HEIGHT / 10))
  606.         {
  607.         speed = WORLD_HEIGHT / 10;
  608.         }
  609.  
  610.         /* Delay between missiles. */
  611.         delay = 2000 - ((level-1)*200);
  612.         if (delay < 500)
  613.         {
  614.         delay = 500;
  615.         }
  616.  
  617.         /* Number of missiles. */
  618.         missileCount = 5 + ((level-1)*5);
  619.  
  620.         /* Number of shots. */
  621.         shotCount = missileCount * 2;
  622.  
  623.         /* Wait until missiles are ready. */
  624.         missileSemaphore.take ();
  625.         
  626.         while (missileCount > 0)
  627.         {
  628.         try
  629.         {
  630.             Thread.sleep (delay);
  631.         }
  632.         catch (InterruptedException e)
  633.         {
  634.         }
  635.         
  636.         /* Fire a missile. */
  637.         createMissile (speed);
  638.         missileCount--;
  639.         }
  640.  
  641.         /* Wait until missiles are dead. */
  642.         missileSemaphore.take ();
  643.         missileSemaphore.give ();
  644.  
  645.         /* See if any new bases can be created. */
  646.         n = score - newBases*POINTS_NEW_BASE;
  647.         while (countBases () < BASES && n >= POINTS_NEW_BASE)
  648.         {
  649.         createBase ();
  650.         n -= POINTS_NEW_BASE;
  651.         newBases++;
  652.         }
  653.  
  654.         /* Game over? */
  655.         n = countBases ();
  656.         if (n == 0)
  657.         {
  658.         break;
  659.         }
  660.         else
  661.         {
  662.         score += n * POINTS_BASE;
  663.         }
  664.     }
  665.  
  666.     playing = false;
  667.     
  668.     if (music != null)
  669.     {
  670.         music.stop ();
  671.     }
  672.  
  673.     /* Destroy the world! */
  674.     for (int i = 0; i < 5; i++)
  675.     {
  676.         int x = (int) (Math.random () * WORLD_WIDTH);
  677.         int y = (int) (Math.random () * WORLD_HEIGHT);
  678.         things.addElement (new Explosion (this, Color.red, x, y, 500));
  679.     }
  680.  
  681.     messageSemaphore.take ();
  682.     createMessage ("GAME OVER");
  683.     messageSemaphore.take ();
  684.     messageSemaphore.give ();
  685.     }
  686.  
  687.  
  688.     /* Stop all running threads. */
  689.     public void stopThreads ()
  690.     {
  691.     if (things != null)
  692.     {
  693.         Enumeration e;
  694.         e = things.elements ();
  695.         while (e.hasMoreElements ())
  696.         {
  697.         Thing thing = (Thing) e.nextElement ();
  698.         thing.explode ();
  699.         }
  700.     }
  701.     }
  702.  
  703.  
  704.     /* Stop this applet. */
  705.     public void stop ()
  706.     {
  707.     if (music != null)
  708.     {
  709.         music.stop ();
  710.     }
  711.  
  712.     stopThreads ();
  713.  
  714.     if (thread != null)
  715.     {
  716.         thread.stop ();
  717.         thread = null;
  718.     }
  719.     }
  720.  
  721.  
  722.     /* Get all the sounds. */
  723.     void getSounds ()
  724.     {
  725.     loadingSounds = true;
  726.     repaint ();
  727.     
  728.     startSound = getAudioClip (getCodeBase (), "sounds/sub_dive_horn.au");
  729.     applauseSound = getAudioClip (getCodeBase (), "sounds/applause.au");
  730.     missileSound = getAudioClip (getCodeBase (), "sounds/missile.au");
  731.     shotExplosionSound = getAudioClip (getCodeBase (), "sounds/shot.au");
  732.     missileExplosionSound = getAudioClip (getCodeBase (), "sounds/beep_multi.au");
  733.     baseExplosionSound = getAudioClip (getCodeBase (), "sounds/bzzzt.au");
  734.     /* background music is disabled since the sound file is too
  735.            large. */
  736.     music = null; /* getAudioClip (getCodeBase (), "sounds/pink_panther.au"); */
  737.     loadingSounds = false;
  738.     }
  739.  
  740.  
  741.     /* Create the bases. */
  742.     void createBases ()
  743.     {
  744.     for (int i = 0; i < BASES; i++)
  745.     {
  746.         createBase ();
  747.     }    
  748.     }
  749.  
  750.  
  751.     /* Create one base, if possible. */
  752.     void createBase ()
  753.     {
  754.     Enumeration e;
  755.     boolean found;
  756.     int i, x;
  757.  
  758.     for (i = 0, x = BASE_SPACING; i < BASES; i++, x += BASE_WIDTH + BASE_SPACING)
  759.     {
  760.         found = false;
  761.         e = things.elements ();
  762.         while (e.hasMoreElements ())
  763.         {
  764.         Thing thing = (Thing) e.nextElement ();
  765.         if (thing instanceof Base && thing.X == x)
  766.         {
  767.             found = true;
  768.         }
  769.         }
  770.  
  771.         if (!found)
  772.         {
  773.         things.addElement (new Base (this, Color.blue, x,
  774.                         WORLD_HEIGHT - BASE_HEIGHT - 1,
  775.                         BASE_WIDTH,
  776.                         BASE_HEIGHT));
  777.         break;
  778.         }
  779.     }
  780.     }
  781.  
  782.  
  783.     /* Create a shot explosion at x,y. */
  784.     void createShotExplosion (int x, int y)
  785.     {
  786.     if (shots > MAX_SHOTS)
  787.     {
  788.         return;
  789.     }
  790.  
  791.     things.addElement (new ShotExplosion (this, x, y));
  792.     if (shotExplosionSound != null)
  793.     {
  794.         shotExplosionSound.play ();
  795.     }
  796.  
  797.     shots++;
  798.     }
  799.  
  800.     
  801.     /* Create a base explosion at x,y. */
  802.     void createBaseExplosion (int x, int y)
  803.     {
  804.     things.addElement (new BaseExplosion (this, x, y));
  805.     if (baseExplosionSound != null)
  806.     {
  807.         baseExplosionSound.play ();
  808.     }
  809.     }
  810.  
  811.  
  812.     /* Create a missile starting at x,y with speed. */
  813.     void createMissile (int x1, int y1, int speed)
  814.     {
  815.     int x2, y2;
  816.     Color color = Color.red;
  817.     
  818.     x2 = (int)(Math.random () * WORLD_WIDTH);
  819.     if (x2 == 0) x2 = 1;
  820.     y2 = WORLD_HEIGHT - 2;
  821.     
  822.     things.addElement (new Missile (this, color, x1, y1, x2, y2, speed));
  823.     if (missileSound != null)
  824.     {
  825.         missileSound.play ();
  826.     }
  827.     }
  828.  
  829.  
  830.     /* Create a missile at a random location with speed. */
  831.     void createMissile (int speed)
  832.     {
  833.     int x1, y1;
  834.  
  835.     x1 = (int)(Math.random () * WORLD_WIDTH);
  836.     if (x1 == 0) x1 = 1;
  837.     y1 = 2;
  838.  
  839.     createMissile (x1, y1, speed);
  840.     }
  841.  
  842.  
  843.     /* Create a blinking message. */
  844.     void createMessage (String string)
  845.     {
  846.     int w = fontMetrics.stringWidth (string);
  847.     int x = WORLD_WIDTH/2 - w/2;
  848.     int y = WORLD_HEIGHT/2;
  849.  
  850.     things.addElement (new Message (this, string, x, y));
  851.     }
  852.  
  853.  
  854.     /* Handle events. */
  855.     public boolean handleEvent (Event e)
  856.     {
  857.     if (e.id == Event.MOUSE_DOWN) /* mouse click */
  858.     {
  859.         if (playing
  860.         && shotCount > 0
  861.         && e.x >= 0 && e.x <= WORLD_WIDTH
  862.         && e.y >= 0 && e.y <= WORLD_HEIGHT)
  863.         {
  864.         createShotExplosion (e.x, e.y);
  865.         shotCount--;
  866.         }
  867.         else if (!playing)
  868.         {
  869.         newGame ();
  870.         }
  871.         return true;
  872.     }
  873.     else
  874.     {
  875.         return super.handleEvent (e);
  876.     }
  877.     }
  878.  
  879.  
  880.     /* Check all things for a collision with this missile. */
  881.     public void checkCollision (Missile missile)
  882.     {
  883.     Enumeration e;
  884.     int nbases = 0, nexplosions = 0;
  885.  
  886.     e = things.elements ();
  887.     while (e.hasMoreElements ())
  888.     {
  889.         Thing thing = (Thing) e.nextElement ();
  890.         if (thing != missile && thing.isAlive ())
  891.         {
  892.         if (thing.hit (missile))
  893.         {
  894.             thing.explode ();
  895.             
  896.             if (thing instanceof Base)
  897.             {
  898.             nbases++;
  899.             }
  900.             else if (thing instanceof ShotExplosion)
  901.             {
  902.             nexplosions++;
  903.             score += POINTS_MISSILE;
  904.             if (missileExplosionSound != null)
  905.             {
  906.                 missileExplosionSound.play ();
  907.             }
  908.             }
  909.         }
  910.         }
  911.     }
  912.  
  913.     if (nbases > 0)
  914.     {
  915.         createBaseExplosion (missile.X, missile.Y);
  916.     }
  917.  
  918.     if (nbases > 0 || nexplosions > 0)
  919.     {
  920.         missile.explode ();
  921.     }
  922.     }
  923.  
  924.  
  925.     /* Update (paint, erase) things. */
  926.     public synchronized void updateThings (Graphics g)
  927.     {
  928.     int i, j;
  929.     Enumeration e;
  930.  
  931.     e = things.elements ();
  932.     while (e.hasMoreElements ())
  933.     {
  934.         Thing thing = (Thing) e.nextElement ();
  935.         if (thing.isAlive ())
  936.         {
  937.         thing.paint (g);
  938.         
  939.         if (thing instanceof Missile)
  940.         {
  941.             checkCollision ((Missile) thing);
  942.         }
  943.         }
  944.         else
  945.         {
  946.         thing.erase (g);
  947.         things.removeElement (thing);
  948.  
  949.         if (thing instanceof Message)
  950.         {
  951.             messageSemaphore.give ();
  952.         }
  953.         else if (thing instanceof ShotExplosion)
  954.         {
  955.             shots--;
  956.         }
  957.         }
  958.     }
  959.     }
  960.  
  961.  
  962.     /* Return the number of remaining missiles. */
  963.     public int countMissiles ()
  964.     {
  965.     Enumeration e;
  966.     int count = 0;
  967.     
  968.     e = things.elements ();
  969.     while (e.hasMoreElements ())
  970.     {
  971.         Thing thing = (Thing) e.nextElement ();
  972.         if (thing instanceof Missile && thing.isAlive ())
  973.         {
  974.         count++;
  975.         }
  976.     }
  977.  
  978.     return count;
  979.     }
  980.     
  981.  
  982.     /* Return the number of remaining bases. */
  983.     public int countBases ()
  984.     {
  985.     Enumeration e;
  986.     int count = 0;
  987.     
  988.     e = things.elements ();
  989.     while (e.hasMoreElements ())
  990.     {
  991.         Thing thing = (Thing) e.nextElement ();
  992.         if (thing instanceof Base && thing.isAlive ())
  993.         {
  994.         count++;
  995.         }
  996.     }
  997.  
  998.     return count;
  999.     }
  1000.  
  1001.  
  1002.     /* Draw the borders. */
  1003.     public void updateBorder (Graphics g)
  1004.     {
  1005.     g.setColor (Color.black);
  1006.     g.drawRect (0, 0, WORLD_WIDTH + SCORE_WIDTH - 1, WORLD_HEIGHT - 1);
  1007.     g.drawLine (WORLD_WIDTH, 0, WORLD_WIDTH, WORLD_HEIGHT);
  1008.     }
  1009.  
  1010.  
  1011.     String numberToZeroPaddedString (int number, int length)
  1012.     {
  1013.     StringBuffer s;
  1014.  
  1015.     s = new StringBuffer (Integer.toString (number));
  1016.     while (s.length () < length)
  1017.     {
  1018.         s.insert (0, "0");
  1019.     }
  1020.  
  1021.     return s.toString ();
  1022.     }
  1023.  
  1024.  
  1025.     /* Draw the score. */
  1026.     public synchronized void updateScore (Graphics g)
  1027.     {
  1028.     int h, w;
  1029.     int x, y;
  1030.     int n;
  1031.     StringBuffer s;
  1032.  
  1033.     w = fontMetrics.stringWidth ("00000");
  1034.     h = fontMetrics.getHeight ();
  1035.     x = WORLD_WIDTH + 5;
  1036.     g.setColor (Color.black);
  1037.  
  1038.     y = h;
  1039.     g.drawString (scoreString, x, y);
  1040.  
  1041.     y += h;
  1042.     g.clearRect (x, y - h, w, h);
  1043.     g.drawString (numberToZeroPaddedString (score, 5), x, y);
  1044.  
  1045.     y += 2*h;
  1046.     g.drawString (shotString, x, y);
  1047.  
  1048.     y += h;
  1049.     g.clearRect (x, y - h, w, h);
  1050.     g.drawString (numberToZeroPaddedString (shotCount, 5), x, y);
  1051.     }
  1052.  
  1053.  
  1054.     /* Don't clear the screen; call paint. */
  1055.     public void update (Graphics g)
  1056.     {
  1057.     paint (g);
  1058.     }
  1059.  
  1060.  
  1061.     /* Paint the screen. */
  1062.     public void paint (Graphics g)
  1063.     {
  1064.     if (clearScreen)
  1065.     {
  1066.         g.setColor (Color.lightGray);
  1067.         g.fillRect (0, 0, WORLD_WIDTH + SCORE_WIDTH, WORLD_HEIGHT);
  1068.         clearScreen = false;
  1069.     }
  1070.  
  1071.     if (loadingSounds)
  1072.     {
  1073.         int w = fontMetrics.stringWidth (loadingString);
  1074.         int x = WORLD_WIDTH/2 - w/2;
  1075.         int y = WORLD_HEIGHT/2;
  1076.         g.setColor (Color.black);
  1077.         g.drawString (loadingString, x, y);
  1078.         clearScreen = true;
  1079.     }
  1080.     else if (!playing && (things == null || things.size () == 0))
  1081.     {
  1082.         int w = fontMetrics.stringWidth (welcomeString);
  1083.         int x = WORLD_WIDTH/2 - w/2;
  1084.         int y = WORLD_HEIGHT/2;
  1085.         g.setColor (Color.black);
  1086.         g.drawString (welcomeString, x, y);
  1087.     }
  1088.     else if (things != null)
  1089.     {
  1090.         Graphics gc = g.create (0, 0, WORLD_WIDTH, WORLD_HEIGHT);
  1091.         updateThings (gc);
  1092.  
  1093.         /* No more missiles. */
  1094.         if (missileSemaphore != null && countMissiles () == 0 && missileCount == 0)
  1095.         {
  1096.             missileSemaphore.give ();
  1097.         }
  1098.     }
  1099.     
  1100.     updateBorder (g);
  1101.     updateScore (g);
  1102.     }
  1103. }
  1104.  
  1105. /*
  1106. Local variables:
  1107. eval: (progn (make-local-variable 'compile-command) (setq compile-command (concat "javac " buffer-file-name)))
  1108. End:
  1109. */
  1110.